# -*- coding: utf-8 -*-

from django import forms
import formfields
from django.forms.util import ErrorList
from django.db import transaction

from stream import models, utils

class TagForm(forms.Form):
    tags = formfields.SeparatedStringsField(label='Show only items with these tags') # This is for user input, nothing should be added to db

class AdditionForm(forms.Form):
    '''
    This class operates on metadata given in its descendants to provide them with form_name field which
    acts as type field.
    '''

    def __init__(self, *args, **kwargs):
        super(AdditionForm, self).__init__(*args, **kwargs)
        self.fields.insert(len(self.fields), 'form_name', forms.CharField(max_length = 30, widget = forms.HiddenInput))
        self.fields['form_name'].initial = self.__class__._meta.FORM_NAME

class BookmarkForm(AdditionForm):
    SUBMIT_ACTION_NAME = 'Add'
    link = forms.URLField()
    tags = formfields.TagField()
    published = forms.BooleanField(initial = False, required = False)

    def save(self, user):
        """
        Saves data from form into db. Should be called only after is_valid was called.
        It implements custom save and doesn't call parent's save.
        """
        link = self.cleaned_data['link']
        # If no protocol specified - use HTTP
        if link.find('://') == -1:
            link = 'http://' + link

        tags = self.cleaned_data['tags']

        bookmark = models.BookmarkEntry.objects.get_or_create(link=link)[0]
        if models.Message.objects.filter(entry = bookmark, user = user):
            # TODO: should instead redirect user to editing his bookmark
            return
        message = models.Message.objects.create(entry = bookmark, user = user, published = self.cleaned_data['published'])
        for tag in tags:
            message.tags.add(tag)
        message.save()

    _meta = utils.PropertyBag(FORM_NAME = 'bookmark_form')

class ExternalAdditionForm(AdditionForm):
    def __init__(self, *args, **kwargs):
        super(ExternalAdditionForm, self).__init__(*args, **kwargs)
        self.fields.insert(len(self.fields), 'import_tags', forms.BooleanField(initial = False, required = False))
        self.fields.insert(len(self.fields), 'tags', formfields.TagField())
        self.fields.insert(len(self.fields), 'published', forms.BooleanField(initial = False, required = False))

    def save(self, user):
        kwargs = {}
        for name, value in self.cleaned_data.items():
            if name not in ('form_name', 'import_tags', 'tags', 'published'):
                kwargs[name] = value
        try:
            channel = self.__class__._meta.model.get_or_create(**kwargs)
            channel.createSubscription(user = user, import_tags = self.cleaned_data['import_tags'], tags = self.cleaned_data['tags'], published = self.cleaned_data['published'])
            return True
        except self.__class__._meta.model.NotFoundError, e:
            self._errors[self.__class__._meta.error_field] = ErrorList([e.getMessage()])
            raise

def djangoDBManaged(func):
    import django
    def f(*args, **kwargs):
        django.db.transaction.enter_transaction_management()
        django.db.transaction.managed(True)
        try:
            rs = func(*args, **kwargs)
        except Exception:
            if django.db.transaction.is_dirty():
                django.db.transaction.rollback()
            django.db.transaction.leave_transaction_management()
            raise
        finally:
            if django.db.transaction.is_managed():
                if django.db.transaction.is_dirty():
                    django.db.transaction.commit()
                django.db.transaction.leave_transaction_management()
        return rs
    # So logging gets the right call info whatever the decorator order is
    f.__name__ = func.__name__
    f.__doc__ = func.__doc__
    f.__dict__ = func.__dict__
    return f

class OpmlForm(ExternalAdditionForm):
    SUBMIT_ACTION_NAME = 'Upload'
    file = forms.FileField(label = 'OPML file')

    @djangoDBManaged
    def save(self, user):
        from feeds import opml
        channels = opml.parse(self.cleaned_data['file'].read())
        for item in channels:
            try:
                channel = models.FeedChannel.get_or_create(url = item['link'])
                subscription = channel.createSubscription(name = item['title'], user = user, import_tags = self.cleaned_data['import_tags'], published = self.cleaned_data['published'], tags = self.cleaned_data['tags'])
            except Exception, e:
                transaction.rollback()
            else:
                transaction.commit()

    _meta = utils.PropertyBag(FORM_NAME = 'opml_form')

class FeedUrlForm(ExternalAdditionForm):
    SUBMIT_ACTION_NAME = 'Subscribe'
    url = forms.URLField(initial = '', label = 'Feed URL')

    _meta = utils.PropertyBag(FORM_NAME = 'feed_form', model = models.FeedChannel, error_field = 'url')

class DeliciousForm(ExternalAdditionForm):
    SUBMIT_ACTION_NAME = 'Subscribe'
    author = forms.CharField(max_length = 30, label = 'Delicious username')

    _meta = utils.PropertyBag(FORM_NAME = 'delicious_form', model = models.DeliciousChannel, error_field = 'author')

class TwitterForm(ExternalAdditionForm):
    SUBMIT_ACTION_NAME = 'Subscribe'
    author = forms.CharField(max_length = 30, label = 'Twitter username')

    _meta = utils.PropertyBag(FORM_NAME = 'twitter_form', model = models.TwitterChannel, error_field = 'author')

class SubscriptionForm(forms.ModelForm):
    id = forms.IntegerField(widget = forms.HiddenInput)
    tags = formfields.TagField()
    update = forms.BooleanField(initial = False, label = 'Apply to existing messages', required = False)

    class Meta:
        model = models.Subscription
        fields = ('id', 'name', 'tags', 'published', 'favorite')

class FilterForm(forms.ModelForm):
    id = forms.IntegerField(widget = forms.HiddenInput, required = False)
    tags = formfields.TagField()
    subscription = formfields.ForeignKeyField(models.Subscription, widget = forms.HiddenInput)
    words = formfields.ModelSeparatedStringsField(models.Word, 'name')

    class Meta:
        model = models.Filter
